/* main.cpp - entry point for BLACC (Burk Labs' Anachronistic Compiler Compiler)
 *
 * BLACC is designed to be a parser generator that I can easily bend
 * to whatever inconvenient need I have.
 */

#include "common.h"
#include "token.h"
#include "error.h"
#include <stdio.h>
#include <thread>
//#include <stack>
#include "parse.h"

static void  TokenizeAll(unique_ref<TTokens> Tokens, string GrammarFilename,
    int IncludeToken = -1);
static unsigned int     HardwareConcurrency(void);

int main(int ArgCount, char** Args)
    {
    string  GrammarFilename;
//    TToken      NullToken = {nullptr, 0, 0};

//    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    unique_ptr<TTokens> Tokens(new TTokens);

    try {
        // parse command-line so we can get an input file
        Global::ParseArgs(ArgCount, Args, true);
        if(Global::Dump)
            printf("# of threads = %u\n", HardwareConcurrency());

        GrammarFilename = Global::InputFilename();
        if(GrammarFilename.empty())
            Error("MISSING_INPUT_FILE");

        TokenizeAll(Tokens, GrammarFilename);
        Parse(Tokens);
        }
    catch(TError* ErrorParms)
        {
        unique_ptr<TError> Errors(ErrorParms);
        DUMPVERBOSE("Caught error: '%s'\n", ErrorParms->GetName().c_str());
        Tokens->Freeze();
        ErrorParms->WriteToFile(Tokens, stdout);
#if 0
        system("time /t");
#define LOOPS (1024*1024*16)
        for(int i=0; i < LOOPS; ++i)
            for(int j=0; j < Tokens->End(); ++j)
                if(Tokens->Get(j).Type == 999)
                    printf("!!\n");
        system("time /t");
        auto Slice = Tokens->Slice(0,-1);
        for(int i=0; i < LOOPS; ++i)
            {
//            for(auto Token: Tokens->Slice(0, -1))
            for(auto Token: Slice)
                if(Token.Type == 999)
                    printf("!!\n");
            }
        system("time /t");
        for(int i=0; i < LOOPS; ++i)
            {
//            for(auto Token: Tokens->Slice(0, -1))
            auto End = Slice.end();
            for(auto Iter=Slice.begin(); Iter < End; ++Iter)
                if(Iter->Type == 999)
                    printf("!!\n");
            }
        system("time /t");
#endif
        }
    }


/* TokenizeAll() - Get to the point we have a set of input tokens ready to parse.
 *
 * This function will, if it encounters no errors, completely tokenize all of the
 * input of the designated input file, as well as all files it %include's.
 * It will throw an exception on the first bad token (if any), but only after having
 * tokenized all of the input, %include files as well.
 ****************************************************************************************/
static void TokenizeAll(unique_ref<TTokens> Tokens, string GrammarFilename,
    int IncludeToken)
    {
    TFileTokens FileTokens;
    TToken      Token;

    Tokens->Tokenize(GrammarFilename, IncludeToken);

    FileTokens  = Tokens->GetFile(GrammarFilename);
    assert(FileTokens.Begin >= 0);
    for(auto iToken=FileTokens.Begin; ; ++iToken)
        {
        Token   = Tokens->Get(iToken);
        if(Token.Type == TToken::TKEOF)
            break;
        if(Token.Type == TToken::INCLUDE)
            {
            Token   = Tokens->Get(++iToken);
            if(Token.Type != TToken::WHITESPACE)
                Error("EXPECTING_WHITESPACE_AFTER_INCLUDE",
                    "Token", iToken);
            Token   = Tokens->Get(++iToken);
            if(Token.Type != TToken::QUOTED)
                Error("EXPECTING_QUOTED_STRING_AFTER_INCLUDE",
                    "Token", iToken);
            auto Filename = Token.Unquote();
            TokenizeAll(Tokens, Filename.get(), iToken);
            }
        }
    }

/* HardwareConcurrency() - How much parallelism does the hardware have?
 *
 * Due to a very painful bug in Visual C++, we use a wrapper function for
 * what should be a simple library call.
 */
static unsigned int HardwareConcurrency(void)
    {
    static unsigned int ThreadCount;

    if(ThreadCount == 0)
        {
        try {
            // compiler thinks next line can't throw exception (true, except for bug)
            ThreadCount = std::thread::hardware_concurrency();
            // have to convince compiler an exception could be thrown...
            unique_ptr<char[]>    Dummy(new char[1]);
            }
        catch (...)
            {
            fprintf(stderr,
                "Can't happen: hardware_concurrency() threw an exception.\n"
                "Are you running under Windows/AppVerifier with HighVersionLie checked?\n"
                "If so, you may want to do something like:\n"
                "    appverif -disable HighVersionLie -for blacc.exe\n"
                );
            throw;
            }
        // note that 0 is essentially "don't know"
        if(ThreadCount == 0)
            ThreadCount = 1;
        }

    return ThreadCount;
    }
